package gui;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.Sprite;
import logic.Card;
import logic.Engine;
import logic.EngineBluetoothGame;
import logic.EngineSinglePlayer;
import logic.LogicHelper;
import logic.MainMidlet;
import logic.Point;

/**
 * class representing playing board
 * @author Rekin
 *
 */
public class Board extends Canvas{
	
	public static boolean deviceBuild=true;
	public static int deviceOffset = 50;  //50
	private Engine engine;
	//private Point globalAnchorPoint; //anchor for all drawed images 
	private Point cardsAnchorPoint; //anchor for all drawed cards 
	private Image background;
	private Sprite[] mainPlayerCards;
	private Sprite[] multiplayerPlayerCards;
	private IngameMenuCanvas ingameMenu;
	private CanvasButton[] buttons;
	
	/**
	 * constructor for making board with defined parameters
	 * @param eng - Engine class object with parameters for newly created Board
	 */
	public Board(Engine eng){
		setFullScreenMode(true);
		engine = eng;
		buttons = new CanvasButton[4];
		buttons[0]=new CanvasButton(50, 25, new Point(getWidth()-50,0), "Menu");
        
        initGameVariables();
        
        if(getWidth()<185){
        	buttons[1] = new CanvasButton(45,22,new Point(0, cardsAnchorPoint.getY()-10-30), "Call");
            buttons[2] = new CanvasButton(65,22,new Point(45, cardsAnchorPoint.getY()-10-30), "Double");
            buttons[3] = new CanvasButton(45,22,new Point(110, cardsAnchorPoint.getY()-10-30), "Drop");
        }
        else if(deviceBuild){
        	buttons[0]=new CanvasButton(90, 45, new Point(getWidth()-90,0), "Menu");
        	buttons[1] = new CanvasButton(90,45,new Point(5, cardsAnchorPoint.getY()-10-80), "Call");
            buttons[2] = new CanvasButton(120,45,new Point(115, cardsAnchorPoint.getY()-10-80), "Double");
            buttons[3] = new CanvasButton(90,45,new Point(260, cardsAnchorPoint.getY()-10-80), "Drop");
        }
        else{
        	buttons[1] = new CanvasButton(50,22,new Point(5, cardsAnchorPoint.getY()-10-30), "Call");
            buttons[2] = new CanvasButton(80,22,new Point(60, cardsAnchorPoint.getY()-10-30), "Double");
            buttons[3] = new CanvasButton(50,22,new Point(145, cardsAnchorPoint.getY()-10-30), "Drop");
        }
        
        revalidateButtons();
        repaint();
	}
	
	/**
	 * @Override
	 * overriden paint from Canvas class for painting the game board
	 */
	protected void paint(Graphics g) {
		if(background!=null){
			g.drawImage(background, 0, 0, Graphics.TOP|Graphics.LEFT);
		}
		paintMultiplayerCards(g);
		//buttons
		for(int i=0;i<4;++i){
			if(buttons[i].isEnabled()){
				buttons[i].paintMe(g);
			}
		}
		//names + money
		g.setColor(250,250,250);
		if(engine.getPlayerToMove()==0){
			g.setColor(240,210,10);
		}
		g.drawString(engine.getPlayer(0).getName() + "("+
				engine.getPlayer(0).getMoney()+"$)", 5 , cardsAnchorPoint.getY() - 40 -20 -10 -deviceOffset, Graphics.TOP|Graphics.LEFT);
		g.setColor(250,250,250);
		for(int i =1; i<engine.getPlayerNumber(); ++i){
			if(engine.getPlayer(i).isBankrupt()){
				g.drawString(engine.getPlayer(i).getName()+ "-bankrupt!"
						,5 , (i-1)*20*2 +5 +(i*deviceOffset/2), Graphics.TOP|Graphics.LEFT);
				continue;
			}
			if(i==engine.getPlayerToMove()){
				g.setColor(240,210,10);
			}
			g.drawString(engine.getPlayer(i).getName()+ "("+
					engine.getPlayer(i).getMoney()+"$)",5 , (i-1)*20*2 +5 +(i*deviceOffset/2), Graphics.TOP|Graphics.LEFT);
			g.setColor(250,250,250);
		}
		//main player cards
		for(int i=0;i<5;++i){
			mainPlayerCards[i].paint(g);
		}
		
		//paint result strings - empty if no result round
		if(engine.getCurrentRound()==4){
			paintMultiplayerCards(g);
			g.setColor(200,200,50);
			g.drawString(engine.getPlayer(0).getSeqTxt(),
					5 , cardsAnchorPoint.getY()-10-30 , Graphics.TOP|Graphics.LEFT);
			for(int i=1; i<engine.getPlayerNumber(); ++i){
				if(engine.getPlayer(0).isInGame()){
					if(!engine.getPlayer(i).isBankrupt())
						g.drawString(engine.getPlayer(i).getSeqTxt(),
								5 , (i-1)*20*2 +25 +(i*deviceOffset/2), Graphics.TOP|Graphics.LEFT);
				}
				else{
					if(engine.getPlayer(i).isInGame() && !engine.getPlayer(i).isBankrupt()){
						g.drawString("-- cards not shown --",
								5 , (i-1)*20*2 +25 +(i*deviceOffset/2), Graphics.TOP|Graphics.LEFT);
					}
				}
			}
		}
		
		//dropped
		for(int i=1; i<engine.getPlayerNumber(); ++i){
			if(!engine.getPlayer(i).isInGame()){
			g.drawString(engine.getPlayer(i).getSeqTxt(),
					5 , (i-1)*20*2 +25 +(i*deviceOffset/2), Graphics.TOP|Graphics.LEFT);
			}
		}
		//paint stake
		g.drawString("On board "+engine.getMoneyOnBoard()+"$"+" ("+engine.getCurrentStake()+"$)", 
				getWidth()/4 , (int)(getHeight()*0.45), Graphics.TOP|Graphics.LEFT);
		
		//winner
		if(engine.getCurrentRound()==4){
			g.drawString("Winner: "+engine.getWinnerName(), 
					getWidth()/4 , (int)(getHeight()*0.50), Graphics.TOP|Graphics.LEFT);
		}
	}
	
	
	/**
	 * method for initiation second player's cards in multiplayer game in final round
	 */
	private void initMultiplayerCards(){
		multiplayerPlayerCards=new Sprite[5];
		Card c;

		for(int i=0;i<5;++i){
			c=engine.getPlayer(1).getCard(i);
						
			multiplayerPlayerCards[i]=createSpriteCard(c.getColor(), c.getFigure()-2);
			if(deviceBuild)
				multiplayerPlayerCards[i].setPosition(cardsAnchorPoint.getX()+ 
						(i*(multiplayerPlayerCards[i].getWidth()+LogicHelper.cardsHorisontalSpacing)),
						(int)(2.5*deviceOffset));
			else
				multiplayerPlayerCards[i].setPosition(cardsAnchorPoint.getX()+ 
						(i*(multiplayerPlayerCards[i].getWidth()+LogicHelper.cardsHorisontalSpacing)),
						20*2 +25 + 5);
		}
	}
	
	/**
	 * method for painting second player's cards in final round
	 * @param g graphics context
	 */
	private void paintMultiplayerCards(Graphics g){
		if(!engine.getPlayer(0).isInGame() || !engine.getPlayer(1).isInGame()){
			return;
		}
		initMultiplayerCards();
		if(engine instanceof EngineBluetoothGame && engine.getWinnerName().length()>0){
			for(int i=0;i<5;++i){
				multiplayerPlayerCards[i].paint(g);
			}
		}
	}
	
	/**
	 * invokes action after pushing Menu button located on the board
	 */
	private void handleMenuButton(){
		boolean canSave = (engine.getCurrentRound()==0 && engine.getPlayerToMove()==0 && 
							engine.isCanSave()) ? true : false;
		ingameMenu =  new IngameMenuCanvas(canSave, engine);
		MainMidlet.getMidletInstance().setDisplay(ingameMenu);
	}
	
	/**
	 * @Override
	 * for handling touch screen events
	 */
	protected void pointerPressed(int x, int y){
		int buttonIdx=buttonHit(x,y);
		switch(buttonIdx){
		case 0:  //menu
			handleMenuButton();
			return;
		case 1:  //call
			if(buttons[buttonIdx].isEnabled()){
				handleCallButton();
			}
			return;
		case 2:  //double or exchange
			if(buttons[buttonIdx].isEnabled()){
				if(engine.getCurrentRound()!=1)
					handleDoubleButton();
				else
					handleExchangeButton();
			}
			return;
		case 3:  //drop or next
			if(buttons[buttonIdx].isEnabled()){
				if(engine.getCurrentRound()!=4)
					handleDropButton();
				else
					handleNextRoundButton();
			}
			return;
		default:
			break;
		}
		if(engine.getCurrentRound()==1){ //only exchange round
			for(int i=0;i<5;++i){
		        if( LogicHelper.checkClickInside(mainPlayerCards[i], new Point(x,y)) ){
		        	moveCardSprite(mainPlayerCards[i]);
		        	updateCardsSelection(i);
		        	return;
		        }
			}
		}
    }
	
	/**
	 * invokes action after pushing Call button located on the board
	 */
	private void handleCallButton(){
		engine.doLogic(LogicHelper.pressButtonCall);
		if(engine instanceof EngineSinglePlayer){
			revalidateButtons();
			repaint();
		}
	}
	
	/**
	 * invokes action after pushing Double button located on the board
	 */
	private void handleDoubleButton(){
		engine.doLogic(LogicHelper.pressButtonDouble);
		if(engine instanceof EngineSinglePlayer){
			revalidateButtons();
			repaint();
		}
	}
	
	/**
	 * invokes action after pushing Drop button located on the board
	 */
	private void handleDropButton(){
		engine.doLogic(LogicHelper.pressButtonDrop);
		if(engine instanceof EngineSinglePlayer){
			revalidateButtons();
			repaint();
		}
	}
	
	/**
	 * invokes action after pushing Next button located on the board
	 */
	private void handleNextRoundButton(){
		engine.doLogic(LogicHelper.pressButtonNext);
		if(engine.isTotalGameVictory()){
			MainMidlet.getMidletInstance().setDisplay(new VictoryScreen());
			return;
		}
		if(engine.isTotalGameDefeat()){
			MainMidlet.getMidletInstance().setDisplay(new DefeatScreen());
			return;
		}
		newSpritesImages();
		if(engine instanceof EngineSinglePlayer){
			revalidateButtons();
			repaint();
		}
	}
	
	/**
	 * invokes action after pushing Exchange button located on the board
	 */
	private void handleExchangeButton(){
		engine.doLogic(LogicHelper.pressButtonExchange);
		if(engine instanceof EngineSinglePlayer){
			resetCardsToStartingPosition();
			newSpritesImages();
			revalidateButtons();
			repaint();
		}
	}
	
	/**
	 * handles buttons availability and visibility depending on current game sub-round
	 */
	private void revalidateButtons(){
		Point newPoint;
		switch(engine.getCurrentRound()){
		case 0:
		case 3:
			setButtonsAvailability(true, false, true);
			if(deviceBuild){
				newPoint = new Point( 260, buttons[3].getTopLeft().getY());
			}
			else{
				newPoint = new Point( 145, buttons[3].getTopLeft().getY());
			}
			buttons[3].setTopLeft(newPoint);
			buttons[3].setMyLabel("Drop");
			break;
		case 1:
			setButtonsAvailability(false, true, false);
			//buttons[2].setItemSize(buttons[2].getMyWidth()+10, buttons[2].getMyHeight());
			buttons[2].setMyLabel("Exchange");
			break;
		case 2:
			if(engine.getCurrentStake()<=Engine.defaultStake){
				setButtonsAvailability(true, true, true);
				//buttons[2].setItemSize(buttons[2].getMyWidth()-10, buttons[2].getMyHeight());
				buttons[2].setMyLabel("Double");
			}
			else{
				setButtonsAvailability(true, false, true);
				//buttons[2].setItemSize(buttons[2].getMyWidth()-10, buttons[2].getMyHeight());
				buttons[2].setMyLabel("Double");
			}
			break;
		case 4:
			setButtonsAvailability(false, false, true);
			newPoint = new Point( getWidth()-buttons[3].getMyWidth(),
								buttons[3].getTopLeft().getY());
			buttons[3].setTopLeft(newPoint);
			buttons[3].setMyLabel("Next");
			break;
		default:
			break;
		}
	}
	
	/**
	 * method for refreshing buttons and screen in multiplayer game
	 */
	public void doMultiplayerRevalidate(){
		revalidateButtons();
		if(engine instanceof EngineBluetoothGame){
			if(!((EngineBluetoothGame)engine).hasToken()){
				setButtonsAvailability(false, false, false);
			}
		}
		repaint(); //revalidates game state (buttons, players money, on board money etc.)
	}
	
	/**
	 * sets choosen buttons to be available/disable
	 * @param but1 availability for Call button
	 * @param but2 availability for Double/Exchange button
	 * @param but3 availability for Drop/Next button
	 */
	private void setButtonsAvailability(boolean but1, boolean but2, boolean but3){
		buttons[1].setEnabled(but1);
		buttons[2].setEnabled(but2);
		buttons[3].setEnabled(but3);
	}
	
	/**
	 * check if any button was hit by examining the coordinates of pointer press
	 * @param x horizontal coordinate of pointer press
	 * @param y vertical coordinate of pointer press
	 * @return button that was hit or -1 if none
	 */
	private int buttonHit(int x, int y){
		for(int i=0;i<4;++i){
			int ex1=buttons[i].getTopLeft().getX();
			int ex2=ex1 +buttons[i].getMyWidth();
			int ey1=buttons[i].getTopLeft().getY();
			int ey2=ey1 + buttons[i].getMyHeight();
			if(x>=ex1 && x<=ex2 && y>=ey1 && y<=ey2){
				return i;
			}
		}
		return -1;
	}
	
	/**
	 * moves cards in exchange round
	 * @param s Sprite image to be moved
	 */
	private void moveCardSprite(Sprite s){
		int val=-1;
		if(isSpriteUpperPositions(s)){
			val=1;
		}
		s.move(0, val*LogicHelper.cardSelectPositionChange);
	}
	
	/**
	 * resets all cards positions to default (after exchange round)
	 */
	public void resetCardsToStartingPosition(){
		for (int i = 0; i < mainPlayerCards.length; i++) {
			resetCardPosition(mainPlayerCards[i]);
		}
	}
	
	/**
	 * resets one card position to default (after exchange round)
	 * @param s Sprite image to be moved
	 */
	private void resetCardPosition(Sprite s){
		if (isSpriteUpperPositions(s)) {
			s.move(0, LogicHelper.cardSelectPositionChange);
		}
	}
	
	/**
	 * checks if card Sprite is selected and in upper position
	 * @param s Sprite image that is checked
	 * @return true is Sprite is selected and in upper position otherwise false
	 */
	private boolean isSpriteUpperPositions(Sprite s){
		if(s.getY()<cardsAnchorPoint.getY()){
			return true;
		}
		return false;
	}
	
	/**
	 * counts anchor points, which are generic values dependant on screen size
	 * all other elements are posiotioned dependent to those points
	 */
	private void countAnchorPoints(){
		//globalAnchorPoint = new Point(10,10);
		Image img = LogicHelper.createImageFromResources("/cards/00.png", this);
		int imW= img.getWidth();
		int imH= img.getHeight();
		while( (imW + LogicHelper.cardsHorisontalSpacing)*5 > getWidth() ){
			imW*=0.8;
			imH*=0.8;
		}
		cardsAnchorPoint = new Point(5,getHeight()-imH-10);
	}
	
	/**
	 * inits board field elements
	 */
	private void initGameVariables(){
		background=LogicHelper.createImageFromResources("/background.png",this);
		background=LogicHelper.resizeImage(background, getWidth(), getHeight());
		countAnchorPoints();
		initPlayerCardsSprites();
	}
	
	/**
	 * creates Sprite images for player cards
	 */
	private void initPlayerCardsSprites(){
		mainPlayerCards=new Sprite[5];
		Card c;

		for(int i=0;i<5;++i){
			c=engine.getPlayer(0).getCard(i);
						
			mainPlayerCards[i]=createSpriteCard(c.getColor(), c.getFigure()-2);
			mainPlayerCards[i].setPosition(cardsAnchorPoint.getX()+ 
					(i*(mainPlayerCards[i].getWidth()+LogicHelper.cardsHorisontalSpacing)),
					cardsAnchorPoint.getY());
		}
	}
	
	/**
	 * recreates player's images after exchange or new round
	 */
	public void newSpritesImages(){
		Card c;
		for(int i=0;i<5;++i){
			c=engine.getPlayer(0).getCard(i);
			mainPlayerCards[i]=createSpriteCard(c.getColor(), c.getFigure()-2);
			mainPlayerCards[i].setPosition(cardsAnchorPoint.getX()+ 
					(i*(mainPlayerCards[i].getWidth()+LogicHelper.cardsHorisontalSpacing)),
					cardsAnchorPoint.getY());
		}
	}
	
	/**
	 * updates card selection flag
	 * @param cardIndex card index from player's cards to be updated
	 */
	private void updateCardsSelection(int cardIndex){
		if(!engine.getPlayer(0).getCard(cardIndex).isSelected()){
			engine.getPlayer(0).getCard(cardIndex).setSelected(true);
		}
		else{
			engine.getPlayer(0).getCard(cardIndex).setSelected(false);
		}
		repaint();
	}
	
	/**
	 * creates single Sprite image for card depending on desired color and figure
	 * @param color card color
	 * @param figure card figure
	 * @return created Sprite image
	 */
	private Sprite createSpriteCard(int color, int figure){
		String file;
		Image im;
		file="/cards/"+color+figure+".png";
		im=LogicHelper.createImageFromResources(file, this);
		Image imResized=doResizeIfNeeded(im);
		return new Sprite(imResized);
	}
	
	/**
	 * resizes images when it is neccessary
	 * @param im image to be resized
	 * @return resized image
	 */
	private Image doResizeIfNeeded(Image im){
		Image img=im;
		Image img2;
		while( (img.getWidth()+ LogicHelper.cardsHorisontalSpacing)*5 > getWidth() ){
			img2 = LogicHelper.resizeImage(img, (int)(0.8*img.getWidth()), (int)(0.8*img.getHeight()));
			img=img2;
		}
		return img;
	}
	
	/**
	 * @Unused
	 * prepared for handling no touch screen devices
	 */
	protected void keyPressed(int keyCode){
		//someday do touch device handle
    	switch(keyCode){
    	case Canvas.KEY_NUM5:
    	case Canvas.FIRE:
    		break;
    	case Canvas.KEY_NUM2:
    	case Canvas.UP:
    		break;
    	case Canvas.KEY_NUM8:
    	case Canvas.DOWN:
    		break;
    	default:
    		break;
    	}
    }
	
}
